﻿/*
列表初始化
	C++11以后想统一初始化方式，试图实现一切对象皆可用{} 初始化，{}初始化也叫做列表初始化。
	内置类型支持，自定义类型也支持，自定义类型本质是类型转换，中间会产生临时对象，最后优化了以后变成直接构造。
	
	{}初始化的过程中，可以省略掉 =
 
	C++11列表初始化的本意是想实现一个大统一的初始化方式，其次他在有些场景下带来的不少便
	利，如容器push/inset多参数构造的对象时，{}初始化会很方便


临时对象 和 匿名对象的区别：
	匿名对象是主动创建，临时对象是编译器创建      但是都具有常性

单参数构造支持隐式类型转换   



右值引用和移动语义
	C++98 的 C++语法中就有引用的用法，而C++11中新增了的右值引用语法特性，C++11之后，之前学习的引用就叫做左值引用。
	无论左值引用还是右值引用，都是给对象取别名。

左值和右值
	1.左值是一个表示数据的表达式（如变量名或解引用的指针），一般有持久状态，存储在内存中，可以获取他的地址，左值可以出现
 	  赋值符号的左边，也可出现在赋值符号右边。定义时const修饰符后的左值，不能给他赋值，但是可以取他的地址
	2.右值是一个表示数据的表达式，要么是字面值常量，要么是表达式求值过程中创建的临时对象等，右值可以出现在赋值符号的右边，
	  但是不能出现在赋值符号的左边，右值不能取地址
	3.左值的英文简写为lvalue，右值的英文简写为rvalue。传统认为它们分别是left、value、right value的缩写。
	  现代C++中，lvalue 被解释为loactor value的缩写，可意为存储在内存中、有明确存储地址可以取地址的对象，
	  而rvalue 被解释为 read value，指的是那些可以提供数据值，但是不可以寻址，例如：临时变量，字面量常量，
	  存储于寄存器中的变量等，也就是说左值和右值的核心区别就是能否取地址。



左值引用：Type& x            右值引用：Type&& y
	Type& r1 = x;       这个就是左值引用，左值引用就是给左值取别名
	Type&& rr1 = y;     这个就是右值引用，右值引用就是给右值取别名

*  左值引用不能直接引用右值，但是const左值引用可以引用右值
*  右值引用不能直接引用左值，但是右值引用可以引用move(左值)
template<class T> typename remove_reference<T>::type&& move (T&& arg)

*   move是库里面的一个函数模板，本质内部是进行强制类型转换，当然他还涉及一些引用折叠的知识
*   需要注意的是变量表达式都是左值属性，也就意味着一个右值被右值引用绑定后，右值引用变量变量表达式的属性是左值
*   语法层面看，左值引用和右值引用都是取别名，不开空间。从汇编底层的角度看下面代码中r1和rr1
    汇编层实现，底层都是用指针实现的，没什么区别。底层汇编等实现和上层语法表达的意义有时是
	背离的，所以不能混到一起去理解，互相佐证，这样反而是陷入迷途



引用延长生命周期
	右值引用可以用于临时对象延长生命周期，const的左值引用也能延长临时对象生存期，但这些对象无法被修改
	注意是 临时对象 和 匿名对象 的生命周期

*  C++98中，实现一个const左值引用作为参数的函数，那么实参传递左值和右值都可以匹配。
*  C++11以后，分别重载左值引用、const左值引用、右值引用作为形参的f函数，那么实参是左值会
   匹配f(左值引用)，实参是const左值会匹配f(const 左值引用)，实参是右值会匹配f(右值引用)。
*  右值引用变量在用于表达式时属性是左值，这个设计这里有些怪异

右值可以用const 修饰的左值进行接收
Linux注意 有的头文件是需要额外包一下

引用三大好处						<1>						<2>							 <3>operator[]
左值引用主要使用场景是在函数中  左值引用传参 和 左值引用传返回值时减少拷贝，同时还可以修改实参和修改返回对象的价值。
左值引用已经解决大多数场景的拷贝效率问题，但是有些场景不能使用传左值引用返回，如addStrings和generate函数，
C++98中的解决方案只能是被迫使用输出型参数解决。

C++11以后这里可以使用右值引用做返回值解决吗？
是不可能的，因为这里的本质是返回对象是一个局部对象，函数结束这个对象就析构销毁了，右值引用返回也无法改变对象已经析构销毁的事实。

*  移动构造和移动赋值  如果编译器没有进行合三为一，拷贝构造就会变成移动构造，拷贝赋值就会变成移动赋值    效率都会得到提高
*  总之：右值引用区分实现出了移动构造和移动赋值，当传值返回是右值对象就不用拷贝了，直接移动资源

*  如果一个变量出了作用域还在那么就可以使用引用返回    但是如果出了作用域不在那就不能使用引用返回

*  1.左值引用和右值引用最终目的都是减少拷贝，提高效率
*  2.左值引用还可以修改参数或者返回值，方便使用


左值引用不足：
	  部分函数返回场景，只能传值返回，不能左值引用返回
	  当前函数局部对象，出了当前函数作用域生命周期到了，也就销毁了，不能用左值引用返回，只能传值返回
	
对于之前不支C++ 11 的时候（用传参输出型参数进行解决问题）
	 这里的传值返回拷贝代价就太大了   需要拷贝							改为  输出型参数  传引用返回	
	vector<vector<int>> generate(int numRows) {   ----->修改成为    void generate(int numRows,vector<vector<int>>& vv){   传引用方式传
		vector<vector<int>> vv(numRows);								vector<vector<int>> vv(numRows);
		for (int i = 0; i < numRows; ++i)								for (int i = 0; i < numRows; ++i)
		{																{		
			vv[i].resize(i + 1, 1);											vv[i].resize(i + 1, 1);			
		}																}
		for (int i = 2; i < numRows; ++i)								for (int i = 2; i < numRows; ++i)
		{																{	
			for (int j = 1; j < i; ++j)										for (int j = 1; j < i; ++j)
			{																{
				vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];						vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
			}																}
		}																}	
		return vv;													}
	}

int main()																int main()
{																		{
	vector<vector<int>> ret = Solution().generate(100);						vector<vector<int>> ret;   
	return 0;																Solution().generate(100,ret);
}																			return 0;
																		定义一个该类型的  然后用该值   作为输出型参数实参传入
																		}

对于传值返回的解决方案：
	1.解决方案一：左值引用只能传值返回（局部对象）   不用返回值，用输出型参数解决  类似于上（不足：一定程度上牺牲了可读性）
	2.解决方案二：编译器的优化   （非标准  不同的编译器优化可能不同）
	3.新标准新语法的处理（右值引用和移动意义）


移动构造和移动赋值
	• 移动构造函数是一种构造函数，类似拷贝构造函数，移动构造函数要求：第一个参数是该类类型的引
	  用，但是不同的是要求这个参数是右值引用，如果还有其他参数，额外的参数必须有缺省值。
	• 移动赋值是一个赋值运算符的重载，他跟拷贝赋值构成函数重载，类似拷贝赋值函数，移动赋值函
	  数要求第一个参数是该类类型的引用，但是不同的是要求这个参数是右值引用。
	• 对于像string/vector这样的深拷⻉的类或者包含深拷⻉的成员变量的类，移动构造和移动赋值才有
	  意义，因为移动构造和移动赋值的第一个参数都是右值引用的类型，他的本质是要“窃取”引用的
	  右值对象的资源，而不是像拷⻉构造和拷⻉赋值那样去拷⻉资源，从提高效率。
	 

右值对象构造，只有拷贝构造，没有移动构造的场景
•  vs2019 debug环境下编译器对拷贝的优化，不优化的情况下，两次拷贝构造，
   编译器优化的场景下连续步骤中的拷⻉合二为一变为一次拷⻉构造。
•  需要注意的是在vs2019的release和vs2022的debug和release，代码优化非常恐怖，会直接将str对象的构造，
   str拷贝构造临时对象，临时对象拷⻉构造ret对象，合三为一，变为直接构造。
•  linux下可以将下面代码拷⻉到test.cpp文件，编译时用 g++ test.cpp -fno-elideconstructors
   的方式关闭构造优化，运行结果可以看到没有优化的两次拷⻉。

右值对象构造，有拷贝构造，也有移动构造的场景
•  vs2019 debug环境下编译器对拷贝的优化，不优化的情况下，两次移动构造，编译器优化的场景下连续步骤中的拷贝合二为一变为⼀次移动构造。
•  需要注意的是在vs2019的release和vs2022的debug和release，代码优化非常恐怖，会直接将str对象的构造，str拷贝构造临时对象，临时对象拷贝构造ret对象，
   合三为一，变为直接构造。这个优化要结合局部对象生命周期和栈帧的角度理解
•  linux下可以将代码拷贝到test.cpp文件，编译时用 g++ test.cpp -fno-elideconstructors
   的方式关闭构造优化，运行结果可以看到图1左边没有优化的两次移动。


深拷贝的自定义类行：vector/string/map
	实现移动构造和移动赋值是有很大价值的
浅拷贝的自定义类型：Date / pair<int,int>
	不需要实现移动构造和移动赋值



类型分类
	1.C++11以后，进一步对类型进行了划分，右值被划分纯右值(pure value，简称prvalue)和将亡值
	2.纯右值是指那些字面值常量或求值结果相当于字面值或是⼀个不具名的临时对象
	  纯右值和将亡值C++11中提出的，C++11中的纯右值概念划分等价于C++98中的右值。
	3.将亡值是指返回右值引用的函数的调用表达式和转换为右值引用的转换函数的调用表达
    4.泛左值(generalized value，简称glvalue)，泛左值包含将亡值和左值。


引用折叠(引用的引用)
	1.C++中不能直接定义引用的引用如 int& && r = i; 这样写会直接报错，通过模板或typedef 中的类型操作可以构成引用的引用。
	2.通过模板或typedef 中的类型操作可以构成引用的引用时，这时C++11给出了⼀个引用折叠的规则：右值引的右值引用折叠成
	  右值引用，所有其他组合均折叠成左值引用。
	3.Function(T&&t) 函数模板程序中。假设实参是int右值，模板参数T的推导int，实参是int左值，模板参数T的推导int&，再结合
	  引用折叠规则，就实现了实参是左值，实例化出左值引用版本形参的Function，实参是右值，实例化出右值引用版本形参的Function


完美转发（保持原有它的属性往下一层传递）
	Function(T&& t)函数模板程序中，传左值实例化以后是左值引用的Function函数，传右值实例化
	以后是右值引用的Function函数。

	变量表达式都是左值属性，也就意味着一个右值被右值引用绑定后，右值引用变量表达式的属性是左值，也就是说
	Function函数中t的属性是左值，那么我们把t传递给下一层函数Fun，那么匹配的都是左值引用版本的Fun函数。
	这里我们想要保持t对象的属性，就需要使用完美转发实现。

	完美转发forward本质是一个函数模板，他主要还是通过引用折叠的方式实现，下面示例中传递给Function的实参是右值，
	T被推导为int，没有折叠，forward内部t被强转为右值引用返回；传递给Function的实参是左值，T被推导为int&，
	引用折叠为左值引用，forward内部t被强转为左值引用返回。

*/